package org.reborn.engine.render.util

import _root_.javax.media.opengl.GL
import _root_.javax.media.opengl.GL._
import _root_.javax.media.opengl.GL2
import _root_.javax.media.opengl.GL2._
import _root_.javax.media.opengl.GL2ES2
import _root_.javax.media.opengl.GL2ES2._
import _root_.javax.media.opengl.fixedfunc.GLPointerFunc._

import org.reborn.engine.model.mesh.Mesh
import org.reborn.engine.model.mesh.DrawableMesh
import org.reborn.engine.model.mesh.VertexData
import org.reborn.engine.util.Registry
import org.reborn.engine.enums._
import ArrayType._
import FaceMode._
import UpdateFrequency._

import java.nio.Buffer

object RenderMeshVertexArray extends RenderMeshSupport{
  protected override val functionList = List(
    "glDrawElements",
    "glVertexPointer",
    "glNormalPointer",
    "glColorPointer",
    "glTexCoordPointer",
    "glClientActiveTexture")
  
  def createRenderer:RenderMesh = new RenderMeshVertexArray();
}

class RenderMeshVertexArray extends RenderMesh{
  protected val meshRegistry:Registry[DrawableMesh] = Registry.SimpleRegistry;
  
  def register(gl:GL2,mesh:Mesh,updateFreq:UpdateFrequency):Int = {
    val drawableMesh = new DrawableMesh();
    drawableMesh.updateData(mesh)
    meshRegistry.register(drawableMesh);
  }
  
  def unregister(gl:GL2,meshID:Int):Unit = {
    meshRegistry.unregister(meshID);
  }
  
  def render(gl:GL2,id:Int):Unit = {
    import gl._

    //Retrieve the mesh that was registered previously
    val mesh:DrawableMesh = meshRegistry.get(id).get

    //Determine what types of faces we are drawing (Points,Lines,Tris,Quads)
    val faceMode = getFaceMode(mesh)
    
    
    enableAvailableClientStates(gl,mesh)	//Enable arrays for any vertex data we have
    glDrawElements(faceMode, mesh.faces * mesh.verticesPerFace, GL_UNSIGNED_INT, mesh.getFacesBuffer.rewind);	//Render the faces
    disableEnabledClientStates(gl,mesh)		//Disable any enabled arrays
  }
  
  def update(gl:GL2,id:Int,newMesh:Mesh):Boolean = {
    //Retrieve the mesh that was registered previously
    val mesh = meshRegistry.get(id)
    
    if(mesh isEmpty) return false;
    
    mesh.get.updateData(newMesh)

    return true;
  }
  
  def updateArrayData(gl:GL2,id:Int,fromMesh:Mesh,aType:ArrayType):Boolean = {
    //Retrieve the mesh that was registered previously
    val meshOpt = meshRegistry.get(id)
    if(meshOpt isEmpty) return false;
    val mesh = meshOpt.get
    
    val vArray = mesh(aType)
    
    vArray.updateData(fromMesh(aType))

    return true;
  }
  
  def updateFaceData(gl:GL2,id:Int,fromMesh:Mesh):Boolean = {
    //Retrieve the mesh that was registered previously
    val meshOpt = meshRegistry.get(id)
    if(meshOpt isEmpty) return false;
    val mesh = meshOpt.get
    
    mesh.updateFaceData(fromMesh)

    return true;
  }
  
  //Enable any vertex arrays that are available in the Mesh
  protected def enableAvailableClientStates(gl:GL2,mesh:Mesh):Unit = {
    import gl._
    
    for(aType <- mesh.getAvailableArrayTypes){
      aType match {
        case Vertex => 
          		enableArray(gl,GL_VERTEX_ARRAY,mesh(Vertex),glVertexPointer)
        case Normal => 
          		enableArray3F(gl,GL_NORMAL_ARRAY,mesh(Normal),glNormalPointer)
        case Color => 
          		enableArray(gl,GL_COLOR_ARRAY,mesh(Color),glColorPointer)
        case TexCoord1 => 
          		glClientActiveTexture(GL_TEXTURE0)
          		enableArray(gl,GL_TEXTURE_COORD_ARRAY,mesh(TexCoord1),glTexCoordPointer)
        case TexCoord2 => 
          		glClientActiveTexture(GL_TEXTURE1)
          		enableArray(gl,GL_TEXTURE_COORD_ARRAY,mesh(TexCoord2),glTexCoordPointer)
        case TexCoord3 => 
          		glClientActiveTexture(GL_TEXTURE2)
          		enableArray(gl,GL_TEXTURE_COORD_ARRAY,mesh(TexCoord3),glTexCoordPointer)
        case TexCoord4 => 
          		glClientActiveTexture(GL_TEXTURE3)
          		enableArray(gl,GL_TEXTURE_COORD_ARRAY,mesh(TexCoord4),glTexCoordPointer)
        case _ =>
      }
    }
  }
  
  //Disable any vertex arrays that were enabled in the Mesh
  protected def disableEnabledClientStates(gl:GL2,mesh:Mesh):Unit = {
    import gl._
    
    for(aType <- mesh.getAvailableArrayTypes){
      aType match {
        case Vertex => glDisableClientState(GL_VERTEX_ARRAY)
        case Normal => glDisableClientState(GL_NORMAL_ARRAY)
        case Color => glDisableClientState(GL_COLOR_ARRAY)
        case TexCoord1 => glClientActiveTexture(GL_TEXTURE0); glDisableClientState(GL_TEXTURE_COORD_ARRAY)
        case TexCoord2 => glClientActiveTexture(GL_TEXTURE1); glDisableClientState(GL_TEXTURE_COORD_ARRAY)
        case TexCoord3 => glClientActiveTexture(GL_TEXTURE2); glDisableClientState(GL_TEXTURE_COORD_ARRAY)
        case TexCoord4 => glClientActiveTexture(GL_TEXTURE3); glDisableClientState(GL_TEXTURE_COORD_ARRAY)
        case _ =>
      }
    }
  }
  
  //Setup/enable the specified array
  protected def enableArray(gl:GL2, arrayType:Int, data:VertexData, glFunc:(Int,Int,Int,Buffer)=>Unit):Unit = {
    import gl._

    glEnableClientState(arrayType)
    val dataBuffer = data.getDataBuffer
    dataBuffer.position(0)
    glFunc(data.partsPerVertex,GL_FLOAT,0,dataBuffer)
  }
  
  //A special case of the above function, where the glFunc only takes 3 arguments
  protected def enableArray3F(gl:GL2, arrayType:Int, data:VertexData, glFunc:(Int,Int,Buffer)=>Unit):Unit = {
    import gl._
    
    glEnableClientState(arrayType)
    val dataBuffer = data.getDataBuffer
    dataBuffer.position(0)
    glFunc(GL_FLOAT,0,dataBuffer)
  }
  
}
